/**
 * 6.泛型
 */
/**
 * 一、指定函数参数类型
 */
// 单个泛型
const getArray = <T>(times: number, val: T): T[] => {
    let result: T[] = [];
    for (let i = 0; i < times; i++) {
        result.push(val);
    }
    return result;
}
getArray(3, 3); // 3 => T => number

// 多个泛型
function swap<T, K>(tuple: [T, K]): [K, T] {
    return [tuple[1], tuple[0]]
}

/**
 * 二、函数标注的方式
 */
// 类型别名
type TArray = <T, K>(tuple: [T, K]) => [K, T];
const getArray2: TArray = <T, K>(tuple: [T, K]): [K, T] => {
    return [tuple[1], tuple[0]];
}
// 可以使用类型别名，但是类型别名不能被继承和实现。一般联合类型可以使用类型别名来声明

// 接口
interface IArray {
    <T, K>(typle: [T, K]): [K, T];
}
const getArray3: IArray = <T, K>(tuple: [T, K]): [K, T] => {
    return [tuple[1], tuple[0]]
}
// 能使用 interface 尽量使用 interface

/**
 * 三、泛型接口使用
 */
interface ISum<T> { // 这里的 T 是使用接口的时候传入
    <U>(a: T, b: T): U; // 这里的 U 是调用函数的时候传入
}
// let sum: ISum<number> = (a: number, b: number) => {
//     return 3 as any;
// }

/**
 * 四、默认泛型
 */
interface T2<T = string> {
    name: T
}
// 类型别名
type T22 = T2;
let name1: T22 = { name: "123" }
// 可以指定泛型的默认类型，方便使用

/**
 * 五、类中的泛型
 */
// 创建实例时提供类型
class MyArray<T> { // T => number
    arr: T[] = [];
    add(num: T) {
        this.arr.push(num);
    }
    getMaxNum(): T {
        let arr = this.arr;
        let max = arr[0];
        for (let i = 1; i < arr.length; i++) {
            let current = arr[i];
            current > max ? max = current : null;
        }
        return max;
    }
}
let myArr = new MyArray<number>();

// 校验构造函数类型
// const createClass3 = <T>(clazz: new(name: string, age: number) => T): T => {
//     return new clazz(name, age);
// }
// createClass3<Person2>(Person2)

/**
 * 六、泛型约束
 */
// 泛型必须包含某些属性
interface IWithLength {
    length: number;
}
function getLen<T extends IWithLength>(val: T) {
    return val.length;
}
// console.log(getLen("hello"))
// const sum2 = <T extends number>(a: T, b: T): T => {
//     return (a + b) as T;
// }
// let r4 = sum2<number>(1, 2);

// 返回泛型中指定属性
const getVal = <T, K extends keyof T>(obj: T, key: K): T[K] => {
    return obj[key];
}
